﻿using System;
using System.Globalization;
using System.Linq;
using System.Speech.Synthesis;
using System.Windows.Forms;
using SF.Code.Extension.ComponentHelper;
using SG.Ling.AutoTranslator.Common;

namespace SG.Ling.AutoTranslator.TTS
{
    public partial class SpeakControl : UserControl
    {
        private SpeechSynthesizer _synth;

        public SpeakControl()
        {
            InitializeComponent();
            Disposed += SpeakControlDisposed;
        }

        void SpeakControlDisposed(object sender, EventArgs e)
        {
            try
            {

                if (_settings != null)
                    _settings.WriteToAppData();
            }
                // ReSharper disable EmptyGeneralCatchClause
                // In disposed!
            catch (Exception)
                // ReSharper restore EmptyGeneralCatchClause
            {
            }
        }

        private SpeakControlSettings _settings;

        private void BtnPlayClick(object sender, EventArgs e)
        {
            Speak(fromButton: true);
        }

        private void BtnPauseClick(object sender, EventArgs e)
        {
            Pause();
        }

        private void BtnStopClick(object sender, EventArgs e)
        {
            Stop();
        }

        private Translation.TranslationTuple _currentTranslation;

        public Translation.TranslationTuple CurrentTranslation
        {
            get { return _currentTranslation; }
            set
            {
                if (value == null)
                    return;

                _currentTranslation = value;
                switch (Direction ?? Translation.Direction.Source)
                {
                    case Translation.Direction.Source:
                        CurrentLanguage = _currentTranslation.SourceLanguage;
                        break;

                    case Translation.Direction.Target:
                        CurrentLanguage = _currentTranslation.TargetLanguage;
                        break;
                }

                Speak();
            }
        }

        public Translation.Direction? Direction
        {
            get { return comboBoxDirection.SelectedItem as Translation.Direction?; }
            set
            {
                if (value.HasValue)
                    comboBoxDirection.SelectedItem = value.Value;
            }
        }

        public CultureInfo CurrentLanguage
        {
            get
            {
                var selected = comboBoxVoice.SelectedItem as VoiceDisplay;
                return selected != null ? selected.Culture : null;
            }
            set
            {
                if (value == null)
                    return;

                var items = comboBoxVoice.Items;
                for (var i = 0; i < items.Count; i++)
                {
                    var voice = items[i] as VoiceDisplay;
                    if (voice != null && voice.Culture.LCID == value.LCID)
                    {
                        comboBoxVoice.SelectedIndex = i;
                        break;
                    }
                }
            }
        }

        public string CurrentText
        {
            get
            {
                var current = CurrentTranslation;
                if (current == null)
                    return null;

                switch (Direction ?? Translation.Direction.Source)
                {
                    case Translation.Direction.Source:
                        return current.SourceText;

                    case Translation.Direction.Target:
                        return current.TargetText;
                }

                return null;
            }
        }

        public void Speak(bool fromButton = false)
        {
            if (!fromButton && !SpeakAutomatically) return;

            Stop();

            var text = CurrentText;
            if (!string.IsNullOrWhiteSpace(text))
            {
                SetVoice();
                _synth.SpeakAsync(text);
            }
        }

        public event EventHandler SpeakAutomaticallyChanged; 

        public bool SpeakAutomatically
        {
            get { return checkBoxAutomatically.Checked; }
            set { checkBoxAutomatically.Checked = value; }
        }

        public void Pause()
        {
            switch (_synth.State)
            {
                case SynthesizerState.Speaking:
                    _synth.Pause();
                    break;
            }
        }

        public void Stop()
        {
            switch (_synth.State)
            {
                case SynthesizerState.Paused:
                case SynthesizerState.Speaking:
                    _synth.SpeakAsyncCancelAll();
                    break;
            }
        }

        private void ComboBoxVoiceSelectionChangeCommitted(object sender, EventArgs e)
        {
            SetVoice();
        }

        private string _lastSetVoice;
        private void SetVoice()
        {
            var voice = comboBoxVoice.SelectedItem as VoiceDisplay;

            if (voice == null || voice.Name == _lastSetVoice) return;

            _settings.Direction = Direction ?? 0;
            _settings.Voice = voice.Name;
            _lastSetVoice = voice.Name;
            _synth.SelectVoice(voice.Name);
        }

        private void TrackBarSpeedValueChanged(object sender, EventArgs e)
        {
            var rate = 2 * (trackBarSpeed.Value - 5);
            _synth.Rate = rate;
            label2.Text = string.Format("Speed ({0:d}):", rate);
        }

        private void ButtonRecordClick(object sender, EventArgs e)
        {

            var text = CurrentText;
            if (string.IsNullOrWhiteSpace(text))
                return;

            // Get name of .WAV file - return if user cancels
            saveFileDialog1.Filter = @"WAV files (*.wav)|*.wav|All files (*.*)|*.*";
            var dialogResult = saveFileDialog1.ShowDialog();
            if (dialogResult == DialogResult.Cancel)
                return;
            var wavFilename = saveFileDialog1.FileName;

            // Save speech as .WAV file
            _synth.SetOutputToWaveFile(wavFilename);
            _synth.Speak(text);
            _synth.SetOutputToDefaultAudioDevice();
        }

        private void ComboBoxDirectionSelectionChangeCommitted(object sender, EventArgs e)
        {
            SetVoice();
        }

        private void SpeakControlLoad(object sender, EventArgs e)
        {
            if (ComponentEx.DesignMode) return;

            _synth = new SpeechSynthesizer();
            _settings = SpeakControlSettings.FromAppData();

            SetupVoicesBox();

            SetupDirectionBox();

            SetupAutoSpeak();
        }

        private void SetupAutoSpeak()
        {
            checkBoxAutomatically.Checked = _settings.AutoSpeak;
            checkBoxAutomatically.CheckStateChanged += checkBoxAutomatically_CheckStateChanged;
        }

        void checkBoxAutomatically_CheckStateChanged(object sender, EventArgs e)
        {
            var copy = SpeakAutomaticallyChanged;
            if (copy == null) return;
            
            copy(this, e);
        }

        private void SetupDirectionBox()
        {
            foreach (Translation.Direction direction in Enum.GetValues(typeof (Translation.Direction)))
            {
                comboBoxDirection.Items.Add(direction);

                if (direction == _settings.Direction)
                    comboBoxDirection.SelectedItem = direction;
            }

            if (comboBoxDirection.Items.Count > 0 && comboBoxDirection.SelectedIndex <= 0)
                comboBoxDirection.SelectedIndex = 0;
        }

        private void SetupVoicesBox()
        {
            var installedVoices =
                from voice in _synth.GetInstalledVoices()
                where !voice.VoiceInfo.Name.Contains("Microsoft Simplified Chinese")
                where voice.Enabled
                select new VoiceDisplay(voice);

            foreach (var voice in installedVoices)
            {
                comboBoxVoice.Items.Add(voice);

                if (voice.Name == _settings.Voice)
                    comboBoxVoice.SelectedIndex = comboBoxVoice.Items.Count - 1;
            }

            if (comboBoxVoice.Items.Count > 0 && comboBoxVoice.SelectedIndex < 0)
                comboBoxVoice.SelectedIndex = 0;
        }

        private void CheckBoxAutomaticallyCheckedChanged(object sender, EventArgs e)
        {
            _settings.AutoSpeak = checkBoxAutomatically.Checked;
        }
    }
}
